/*
 * author: Junky0566
 * Zcom WLAN Scan functions: display list of available WLAN
 * networks, and allow the user to connect to a network, and
 * handle wlan state transitions (connected, not connected etc).
 */

/*===========================================================================*/
/*==============  Includes  =================================================*/
/*===========================================================================*/

#include <stdio.h>
#include <assert.h>

#include <spr.h>
#include <debuglog.h>
#include <hw.h>
#include <ui.h>
#include <nethwApi.h>
#include <display.h>
#include <lang.h>
#include <config.h>
#include <configPriority.h>
#include <mainthread.h>
#include <wlanProfile.h>

#include <NetworkManagement.h>


/*===========================================================================*/
/*==============  Data      =================================================*/
/*===========================================================================*/

/* State data */
struct uiWlanState
{
	struct pollerTask iconTimerTask;    /* periodically */

	int connected;                      /* 1/0 -> conn / not conn */
	char connSSID[WLAN_SSID_LEN + 1];   /* ssid we are connected to */
	unsigned char connBSSID[6];         /* bssid we are connected to */
	short connFreq;                     /* channel freq */
	short connRssi;                     /* RSSI in dB */
	short connRssiIcon;                 /* Current icon setting */

	int menuNetCurr;                    /* index of curr displayed network */


	/* 
	* Cached display information
	*/
	const unsigned char *RssiBarTable;
	int NRssiBars;
};
/*private*/ 
struct uiWlanState uiWlanS;

/*
* Lookup table for the rssi threshold, indexed by icon setting number.
*/
static const unsigned char uiWlanIcon2Rssi5Bars[] = 
CFG_WLAN_RSSI_ICON_LEVELS_5BARS;
static const unsigned char uiWlanIcon2Rssi3Bars[] = 
CFG_WLAN_RSSI_ICON_LEVELS_3BARS;


/*
* Here when a given scan is selected... 
*/
static void uiWlanStateSelected(void *data)
{
	/* If selected when nothing displayed, do nothing */
	if ( uiWlanS.menuNetCurr >= ScanList_GetItemsNumber() ) return;

	nethwWlanNetworkInfo *networkP = (nethwWlanNetworkInfo *)ScanList_GetItemNetworkP(uiWlanS.menuNetCurr);

	uiMenuPop(0);
	zcom_uiWlanProfileAdd(networkP);
	return;
}

static void zcom_uiWlanStateScanListWinRepaint(
	UIMenu *menu,
	Pw_GC *gc,
	Pw_Coord y0,
	Pw_Coord y1
	)
{
	Pw_Window *win = Pw_GCGetWindow(gc);
	Pw_Coord  w, h;
	Pw_Coord  sb_w, sb_y0 = y0, sb_y1 = y1;
	char str[UI_MENU_MAXSTR];

	nethwWlanNetworkInfo *networkP = (nethwWlanNetworkInfo *)ScanList_GetItemNetworkP(uiWlanS.menuNetCurr);

	Pw_WindowGetSize(win, &w, &h);
	sb_w = w;
	w -= CFG_SCROLL_BAR_WIDTH;

	Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_LARGE));
	Pw_GCSetColor(gc, pw_black_pixel);

	if (ScanList_GetItemsNumber() == 0) 
	{
		Pw_DrawStringAnchor(gc, 2, y0, 
			langGet(LANG_WlanScan_empty),
			Pw_TextAnchor_TopLeft);
	} 
	else 
	{
		if ( !networkP->ssid[0] ) 
		{
			//junky remove it 
	/*		Pw_DrawStringAnchor(gc, 2, y0,
				langGet(LANG_WlanScan_ssid_hidden),
				Pw_TextAnchor_TopLeft);*/
			return;
		} 
		else 
		{
			//junky 2007-4-24
			static bool LastCanfit = false;
			static void* LastNetworkP = NULL;
			
			int ssidLen = strlen(networkP->ssid); 
			int canfit = Pw_GCStringFit(gc, networkP->ssid, w - 2);
			if (canfit < ssidLen)
			{
				if (LastCanfit || networkP != LastNetworkP)
				{
					zcom_InitScrollingStrStatusForUser();
				}
				LastCanfit = false;
				//debugf("networkP->ssid=%s\n", networkP->ssid);
				zcom_DrawScrollingStringForUser(gc, 2, y0, networkP->ssid, Pw_TextAnchor_TopLeft, canfit);
			}
			else
			{
				LastCanfit = true;
				Pw_DrawStringAnchor(gc, 2, y0, networkP->ssid, Pw_TextAnchor_TopLeft);
			}
			LastNetworkP = (void*) networkP;
		}
		y0 += Pw_GCGetFontHeight(gc);

		spr(str, sizeof(str), langGet(LANG_WlanScan_rssi_val), networkP->rssi);
		Pw_DrawStringAnchor(gc, 2, y0, str, Pw_TextAnchor_TopLeft);
		y0 += Pw_GCGetFontHeight(gc);

		spr(str, sizeof(str), langGet(LANG_WlanScan_chan_val), networkP->chanNumber);
		Pw_DrawStringAnchor(gc, 2, y0, str, Pw_TextAnchor_TopLeft);
		y0 += Pw_GCGetFontHeight(gc);

		/*
		* TODO: display whether we are connected to this network and
		* whether it requires encryption.
		*/

		uiMenuScrollBarDraw(gc, sb_w, sb_y0, sb_y1,
			uiWlanS.menuNetCurr, ScanList_GetItemsNumber());
	}
}


static Pw_Bool zcom_uiWlanStateScanListWinEvents(UIMenu *menu, Pw_Event *ev)
{
	Pw_Window* win = Pw_EventGetWindow(ev);
    int nScanItems = ScanList_GetItemsNumber();

	if (Pw_EventGetType(ev) == Pw_KeyPressEventType)
	{
		Pw_KeyEvent* kev = Pw_EventCastToKeyEvent(ev);
		Pw_uInt keycode = Pw_KeyEventGetKeyCode(kev);

		switch (keycode)
		{
		case KEYSYM_END:
			uiMainWinKeyEnd();
			break;

		case KEYSYM_DOWN:
			if (nScanItems > 0)
			{
    			uiWlanS.menuNetCurr++;
    			
    			if (uiWlanS.menuNetCurr >= nScanItems)
    			{
    				uiWlanS.menuNetCurr = 0;
    			}

                menu->m.user.nPosUser = 0;
    			
        		Pw_WindowRepaint(win);
			}
			break;

		case KEYSYM_UP:
			if (nScanItems > 0) 
			{
				uiWlanS.menuNetCurr--;
				
    			if (uiWlanS.menuNetCurr < 0)
    			{
    				uiWlanS.menuNetCurr = nScanItems - 1;
    			}

                menu->m.user.nPosUser = 0;
    			
        		Pw_WindowRepaint(win);
			}
			break;

		case KEYSYM_SOFT_NEGATIVE:
			uiMenuPop(1);
			break;

		case KEYSYM_SOFT_POSITIVE:
			if (nScanItems > 0)
			{
    			uiWlanStateSelected((void*)uiWlanS.menuNetCurr);
			}
			break;

		default:
			break;
		}
	}

	return TRUE;
}

/*private*/ 
void zcom_uiWlanStateScanStop(void)
{
	ScanList_ScanStop((void*)SCAN_STATUS_ABORT);
}


static void zcom_ScanCompleteCB(void *arg)
{
	ScanList_Scan_Status ScanStatus = (ScanList_Scan_Status)arg;
	uiMenuPop(1);

	if (ScanStatus == SCAN_STATUS_SUCCESS)
	{
		UIMenuUserInit init;
		memset(&init, 0, sizeof(init));

		init.title.textIdx  = LANG_WlanScan_title;
		init.eventHandler   = zcom_uiWlanStateScanListWinEvents;
		init.repaintHandler = zcom_uiWlanStateScanListWinRepaint;
		init.cleanupCB      = (void *)zcom_uiWlanStateScanStop;

		uiMenuSoftKeyPositive(init.softKey, LANG_SoftKey_Select);
		uiMenuSoftKeyNegative(init.softKey, LANG_SoftKey_Back);

		uiMenuUserInit(&init, NULL);
	}
	else if (ScanStatus == SCAN_STATUS_TIMEOUT)
	{
		uiMenuDialogMesgTimed(LANG_WlanScan_title, langGet(LANG_WlanScan_Timeout));
	}
	else if (ScanStatus == SCAN_STATUS_FAILURE)
	{
		uiMenuDialogMesgTimed(LANG_WlanScan_title, langGet(LANG_WlanScan_Failure));
	}
}

static Pw_Bool zcom_uiWlanStateScanningUserWinEvents(UIMenu *menu, Pw_Event *ev)
{
	if (Pw_EventGetType(ev) == Pw_KeyPressEventType)
	{
		Pw_KeyEvent* kev = Pw_EventCastToKeyEvent(ev);
		Pw_uInt keycode = Pw_KeyEventGetKeyCode(kev);

		switch (keycode) 
		{
		case KEYSYM_END:
			ScanList_ScanStop((void*)SCAN_STATUS_ABORT);
			uiMainWinKeyEnd();
			break;

		case KEYSYM_SOFT_NEGATIVE:
			ScanList_ScanStop((void*)SCAN_STATUS_ABORT);
			//uiMenuPop(1);
			break;

		default:
			break;
		}
	}

	return TRUE;
}

static void zcom_uiWlanStateScanningUserWinRepaint(
	UIMenu *menu,
	Pw_GC *gc,
	Pw_Coord y0,
	Pw_Coord y1
	)
{
	Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_LARGE));
	Pw_DrawStringAnchor(
	    gc, 
	    2, 
	    y0, 
		langGet(LANG_WlanScan_searching),
		Pw_TextAnchor_TopLeft
		);
}


static void uiWlanStateRssiIconUpdate(
struct pollerTask *TaskP        /* ignored! may pass as NULL */
	)
{
	/* In order to support different display types, the number of bars
	* (and thus the translation table) must be dynamically determined.
	*/
	if ( ! uiWlanS.RssiBarTable ) {
		uiWlanS.NRssiBars = dispIconNBarsGet( DISP_ICON_SIGNAL_LEVEL );
		switch ( uiWlanS.NRssiBars ) {
case 3:
	uiWlanS.RssiBarTable = uiWlanIcon2Rssi3Bars;
	break;
case 5:
	uiWlanS.RssiBarTable = uiWlanIcon2Rssi5Bars;
	break;
default:
	/* Need another table? */
	return;
		}
	}
	const unsigned char *table = uiWlanS.RssiBarTable;
	int maxbars = uiWlanS.NRssiBars;

	int rssiIcon = -1;  /* -1 for invalid; don't even display antenna */

	if ( uiWlanS.connected ) {
		uiWlanS.connRssi = nethwWlanRssiGet();

		int nbars = maxbars;
		for ( ; nbars > 0 ; nbars-- ) {
			if ( uiWlanS.connRssi > table[nbars-1] ) {

				break;
			}
		}
		rssiIcon = nbars;
	}

	if ( uiWlanS.connRssiIcon != rssiIcon ) {
		uiWlanS.connRssiIcon = rssiIcon;
		dispIconSet(uiDisplay(), DISP_ICON_SIGNAL_LEVEL, uiWlanS.connRssiIcon);
	}
#if CFG_WLAN_RSSI_ICON_UPDATE_SECONDS() > 0
	pollerTaskLongTimeoutRelSeconds( 
		&uiWlanS.iconTimerTask,
		CFG_WLAN_RSSI_ICON_UPDATE_SECONDS() );
#endif
}

void zcom_uiWlanStateScanListMenuStart(void *data)
{
	UIMenuUserInit init;
	memset(&init, 0, sizeof(init));

	init.eventHandler = zcom_uiWlanStateScanningUserWinEvents;
	init.repaintHandler = zcom_uiWlanStateScanningUserWinRepaint;

	uiMenuSoftKeyNegative(init.softKey, LANG_SoftKey_Cancel);

	uiMenuUserInit(&init, NULL);
	uiWlanS.menuNetCurr = 0;
	uiWlanStateRssiIconUpdate(NULL);
	ScanList_ScanStart(zcom_ScanCompleteCB);
}

static void zcom_uiWlanStateEventCB(void *arg)
{
    unsigned short connFreq;
    unsigned char connBssid[6];

    /*
     * Check connected state
     */
    int connected = nethwWlanConnectionGet(&connFreq, connBssid, NULL, NULL);

    if ( uiWlanS.connected == connected ) {
        /*
         * check if we have roamed to a new bssid
         */
        if ( connected 
			&& (memcmp(uiWlanS.connBSSID, connBssid, sizeof(uiWlanS.connBSSID)) || 
			uiWlanS.connFreq != connFreq) ) 
		{
            memcpy(uiWlanS.connBSSID, connBssid, sizeof(uiWlanS.connBSSID));
            uiWlanS.connFreq = connFreq;
        }
    } 
	else 
	{
        if ( connected ) 
		{
            /*
             * disconnected -> connected
             */
            debugf("## disconnected -> connected\n");

            memcpy(uiWlanS.connBSSID, connBssid, sizeof(uiWlanS.connBSSID));
            uiWlanS.connFreq = connFreq;
            /*
             * TODO: display dialog?
             */
            debugf("uiWlanStateEventHandler: connected to bssid 0x%02x:%02x:%02x:%02x:%02x:%02x, connFreq %d\n",
                connBssid[0],
                connBssid[1],
                connBssid[2],
                connBssid[3],
                connBssid[4],
                connBssid[5],
                connFreq);
        } 
		else 
		{
            /*
             * connected -> disconnected
             */
            debugf("## connected -> disconnected\n");

            /*
             * TODO: dipslay dialog?
             */
        }
        uiWlanS.connected = connected;
        uiWlanStateRssiIconUpdate(NULL);
    }
}

void zcom_uiWlanStateInit(void)
{
    ScanList_InstallStateEvent(zcom_uiWlanStateEventCB);

	mainthreadTaskInit(
		&uiWlanS.iconTimerTask,
		"wlanIconTimerTask",
		uiWlanStateRssiIconUpdate
		);

	uiWlanS.connected      = 0;
	uiWlanS.connRssiIcon   = 0;
	uiWlanStateRssiIconUpdate(NULL);
}
